﻿using System.Linq;
using CID.Domain.Exceptions;
using CID.Domain.Operations;
using CID.Tests.Support;
using NUnit.Framework;
using Rhino.Mocks;

namespace CID.Domain.Tests.Operations
{
    [TestFixture]
    class ServerRoleTests : BaseFunctionalTests
    {
        private const string Name = "Server Role Name";
        private const string Description = "Server Role Name";
        private ServerRole serverRole;

        public override void SetUp()
        {
            base.SetUp();
            serverRole = new ServerRole(Name, Description);
        }

        #region Constructor
        [Test]
        public void CreateNewServerRole()
        {
            var serverRole = new ServerRole(Name, Description);

            Assert.AreEqual(Name, serverRole.Name);
            Assert.AreEqual(Description, serverRole.Description);
            Assert.IsTrue(serverRole.Enabled);
            CollectionAssert.IsEmpty(serverRole.Assignments);
            CollectionAssert.IsEmpty(serverRole.Components);
        }
        #endregion

        #region Assign
        [Test]
        public void Assign_NoPreviousAssignment_ShouldSucceed()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var server = StubsBuilder.BuildServerStub();

            serverRole.Assign(server, environment);

            var assignment = serverRole.Assignments.FirstOrDefault(x => (x.Environment.Equals(environment) && x.Server.Equals(server)));
            Assert.IsNotNull(assignment);
            Assert.AreEqual(1, serverRole.Assignments.Count());
            environment.AssertWasCalled(x => x.AddAssignment(assignment));
            server.AssertWasCalled(x => x.AddAssignment(assignment));
        }

        [Test, ExpectedException(typeof(DuplicateAssignmentException))]
        public void Assign_DuplicateAssignment_ShouldThrowException()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var server = StubsBuilder.BuildServerStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);
            serverRole.AddAssignment(assignment);

            serverRole.Assign(server, environment);
        }
        #endregion

        #region Unassign
        [Test]
        public void Unassign_AnotherPreviousAssignment_NothingShouldHappen()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var anotherEnvironmnet = StubsBuilder.BuildEnvironmentStub();
            var server = StubsBuilder.BuildServerStub();
            serverRole.Assign(server, anotherEnvironmnet);
            var assignment = serverRole.Assignments.FirstOrDefault();

            serverRole.Unassign(server, environment);

            Assert.AreEqual(1, serverRole.Assignments.Count());
            anotherEnvironmnet.AssertWasNotCalled(x => x.RemoveAssignment(assignment));
            server.AssertWasNotCalled(x => x.RemoveAssignment(assignment));
        }

        [Test]
        public void Unassign_PreviousAssignment_ShouldSucceed()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var server = StubsBuilder.BuildServerStub();
            serverRole.Assign(server, environment);
            var assignment = serverRole.Assignments.FirstOrDefault();

            serverRole.Unassign(server, environment);

            Assert.AreEqual(0, serverRole.Assignments.Count());
            environment.AssertWasCalled(x => x.RemoveAssignment(assignment));
            server.AssertWasCalled(x => x.RemoveAssignment(assignment));
        }
        #endregion

        #region AddAssignment
        [Test]
        public void AddAssignment_NoPreviousAssignment_ShouldSucceed()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var server = StubsBuilder.BuildServerStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);

            serverRole.AddAssignment(assignment);

            var retrievedAssignment = serverRole.Assignments.FirstOrDefault(x => (x.Environment.Equals(environment) && x.Server.Equals(server)));
            Assert.IsNotNull(retrievedAssignment);
            Assert.AreEqual(1, serverRole.Assignments.Count());
        }

        [Test, ExpectedException(typeof(InvalidAssignmentException))]
        public void AddAssignment_AssignmentForAnotherEnvironment_ShouldThrowException()
        {
            var anotherServerRole = StubsBuilder.BuildServerRoleStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(serverRole: anotherServerRole);

            serverRole.AddAssignment(assignment);
        }

        [Test, ExpectedException(typeof(DuplicateAssignmentException))]
        public void AddAssignment_DuplicateAssignment_ShouldThrowException()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var server = StubsBuilder.BuildServerStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);
            var duplicateAssignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);

            serverRole.AddAssignment(assignment);
            serverRole.AddAssignment(duplicateAssignment);
        }
        #endregion

        #region RemoveAssignment
        [Test]
        public void RemoveAssignment_NoPreviousAssignment_NothingShouldHappen()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var server = StubsBuilder.BuildServerStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);

            serverRole.RemoveAssignment(assignment);

            Assert.AreEqual(0, serverRole.Assignments.Count());
        }

        [Test]
        public void RemoveAssignment_AssignmentPreviouslyAdded_ShouldSucceed()
        {
            var environment = StubsBuilder.BuildEnvironmentStub();
            var server = StubsBuilder.BuildServerStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(environment, server, serverRole);
            serverRole.AddAssignment(assignment);

            serverRole.RemoveAssignment(assignment);

            Assert.AreEqual(0, serverRole.Assignments.Count());
        }

        [Test, ExpectedException(typeof(InvalidAssignmentException))]
        public void RemoveAssignment_AssignmentForAnotherEnvironment_ShouldThrowException()
        {
            var anotherServerRole = StubsBuilder.BuildServerRoleStub();
            var assignment = StubsBuilder.BuildServerAssignmentStub(serverRole: anotherServerRole);

            serverRole.RemoveAssignment(assignment);
        }
        #endregion

        #region AddComponent
        [Test]
        public void AddComponent_ComponentWithNoServerRoleAssigned_ShouldSucceed()
        {
            var component = Development.StubsBuilder.BuildComponentStub();

            serverRole.AddComponent(component);

            Assert.AreEqual(serverRole, component.RequiredServerRole);
            Assert.AreEqual(1, serverRole.Components.Count());
            CollectionAssert.Contains(serverRole.Components, component);
        }

        [Test]
        public void AddComponent_ComponentWithAnotherServerRoleAssigned_ShouldSucceed()
        {
            var component = Development.StubsBuilder.BuildComponentStub();
            var anotherServerRole = StubsBuilder.BuildServerRoleStub();
            component.RequiredServerRole = anotherServerRole;

            serverRole.AddComponent(component);

            Assert.AreEqual(serverRole, component.RequiredServerRole);
            Assert.AreEqual(1, serverRole.Components.Count());
            CollectionAssert.Contains(serverRole.Components, component);
            anotherServerRole.AssertWasCalled(x => x.RemoveComponent(component));
        }

        [Test]
        public void AddComponentTwice_ComponentShouldBeAddedOnlyOnce()
        {
            var component = Development.StubsBuilder.BuildComponentStub();

            serverRole.AddComponent(component);
            serverRole.AddComponent(component);

            Assert.AreEqual(serverRole, component.RequiredServerRole);
            Assert.AreEqual(1, serverRole.Components.Count());
            CollectionAssert.Contains(serverRole.Components, component);
        }
        #endregion

        #region RemoveComponent
        [Test]
        public void RemoveComponent_ComponentNotAdded_NothingShouldHappen()
        {
            var component = Development.StubsBuilder.BuildComponentStub();

            serverRole.RemoveComponent(component);

            Assert.AreEqual(0, serverRole.Components.Count());
        }

        [Test]
        public void RemoveComponent_ComponentAlreadyAdded_ShouldSucceed()
        {
            var component = Development.StubsBuilder.BuildComponentStub();
            serverRole.AddComponent(component);

            serverRole.RemoveComponent(component);

            Assert.AreEqual(0, serverRole.Components.Count());
            Assert.IsNull(component.RequiredServerRole);
        }


        #endregion
    }
}
